home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (c) 1987, 1988 Stanley T. Shebs, University of Utah. */
- /* This program may be used, copied, modified, and redistributed freely */
- /* for noncommercial purposes, so long as this notice remains intact. */
-
- #pragma comment(exestr, "@(#) attack.c 12.1 95/05/09 ")
-
- /* RCS $Header: attack.c,v 1.3 88/07/17 16:18:22 shebs Exp $ */
-
- /* Although conducting xconq combat is rather simple (i.e. try to move */
- /* into the hex), the outcome is under the control of many parameters. */
-
- /* Rules of combat: the attacker hits the defender ("other") unit and its */
- /* occupants, but the damage does not take effect right away. If counter */
- /* attacks are possible in this period, the defender always does so, with */
- /* the same odds. If the defender dies, then the attacker moves into the */
- /* hex. If the attacker dies, nothing happens. If both survive, then the */
- /* attacker may attempt to capture the defender. */
-
- #include "config.h"
- #include "misc.h"
- #include "period.h"
- #include "side.h"
- #include "unit.h"
- #include "map.h"
- #include "global.h"
-
- extern int occdeath[];
-
- char *summarize_units();
-
- /* Buffers for verbal descriptions of units from each other's */
- /* point of view. */
-
- char aabuf[BUFSIZE], aobuf[BUFSIZE], oabuf[BUFSIZE], oobuf[BUFSIZE];
- char hitbuf[BUFSIZE], killbuf[BUFSIZE];
-
- /* Remember what the main units involved are, so display is handled relative */
- /* to them and not to any occupants. */
-
- Unit *amain, *omain;
-
- /* Hits on main units saved up, hits on occupants happen immediately. */
-
- int ahit, ohit;
-
- /* ... but the data is saved anyway, for message generation. */
-
- int occhits[MAXUTYPES], occkills[MAXUTYPES];
-
- /* Return true if the attacker defeated the defender, and can therefore */
- /* try to move into the defender's old position. */
-
- attack(atker, other)
- Unit *atker, *other;
- {
- int u, ax = atker->x, ay = atker->y, ox = other->x, oy = other->y;
- Side *as = atker->side, *os = other->side;
-
- amain = atker; omain = other;
- ahit = ohit = 0;
- for_all_unit_types(u) occhits[u] = occkills[u] = 0;
- attack_unit(atker, other);
- if (period.counterattack || utypes[atker->type].counterable)
- attack_unit(other, atker);
- reckon_damage();
- if (see_exact(as, ax, ay))
- draw_hex(as, ax, ay, TRUE);
- if (see_exact(as, ox, oy))
- draw_hex(as, ox, oy, TRUE);
- if (see_exact(os, ax, ay))
- draw_hex(os, ax, ay, TRUE);
- if (see_exact(os, ox, oy))
- draw_hex(os, ox, oy, TRUE);
- all_see_hex(ax, ay);
- all_see_hex(ox, oy);
- attempt_to_capture_unit(atker, other);
- return (alive(atker) && unit_at(ox, oy) == NULL);
- }
-
- /* Test to see if enough ammo is available to make the attack. */
- /* Need enough of *all* types - semi-bogus but too complicated otherwise? */
-
- enough_ammo(atker, other)
- Unit *atker, *other;
- {
- int r;
-
- for_all_resource_types(r) {
- if (utypes[other->type].hitby[r] > 0 &&
- atker->supply[r] < utypes[atker->type].hitswith[r]) return FALSE;
- }
- return TRUE;
- }
-
- /* Single attack, no counterattack. Check and use ammo - usage independent */
- /* of outcome, but types used depend on unit types involved. */
-
- attack_unit(atker, other)
- Unit *atker, *other;
- {
- int r;
-
- wake_unit(other, FALSE);
- if (alive(atker) && alive(other)) {
- if (enough_ammo(atker, other)) {
- hit_unit(atker, other);
- for_all_resource_types(r) {
- if (utypes[other->type].hitby[r] > 0) {
- atker->supply[r] -= utypes[atker->type].hitswith[r];
- }
- }
- atker->movesleft -= utypes[atker->type].hittime;
- }
- }
- }
-
- /* Make a single hit and maybe hit some passengers also. Power of hit */
- /* is constant, but chance is affected by neutrality, terrain, quality, */
- /* and occupants' protective abilities. If a hit is successful, it may */
- /* have consequences on the defender's occupants, but limited by the */
- /* protection that the transport provides. */
-
- hit_unit(atker, other)
- Unit *atker, *other;
- {
- int chance, terr, hit = 0, a = atker->type, o = other->type;
- Unit *occ;
- Side *as = atker->side;
-
- chance = utypes[a].hit[o];
- if (neutral(atker)) chance += period.neutrality;
- terr = terrain_at(other->x, other->y);
- chance -= (chance * utypes[o].defense[terr]) / 100;
- if (utypes[a].maxquality > 0) {
- chance += ((chance * atker->quality * utypes[a].skillf) /
- utypes[a].maxquality) / 100;
- }
- if (utypes[o].maxquality > 0) {
- chance -= ((chance * other->quality * utypes[o].disciplinef) /
- utypes[o].maxquality) / 100;
- }
- for_all_occupants(other, occ) {
- chance -= (chance * utypes[occ->type].protect[o]) / 100;
- }
- if (probability(chance)) hit = utypes[a].damage[o];
- if (as != NULL) {
- as->atkstats[a][o]++;
- as->hitstats[a][o] += hit;
- }
- if (hit_unit_aux(atker, other, hit)) {
- for_all_occupants(other, occ) {
- if (probability(100 - utypes[o].protect[occ->type])) {
- hit_unit(atker, occ);
- }
- }
- }
- }
-
- /* Do the hit itself. Occupants are always hit/killed immediately, while */
- /* the "main units" of the hexes don't get it till later. In any case, */
- /* messages are delayed. Return true if the victim was hit but not killed. */
-
- hit_unit_aux(atker, other, hit)
- Unit *atker, *other;
- int hit;
- {
- int o = other->type, a = atker->type, chance, i;
- Side *as = atker->side, *os = other->side;
-
- if (hit >= period.nukehit) {
- notify_all("%s has been hit by a nuclear attack!!!",
- unit_handle(NULL, other));
- set_terrain_at(other->x, other->y,
- ttypes[terrain_at(other->x, other->y)].nuked);
- }
- if (hit > 0 && mobile(o)) {
- chance = utypes[o].retreat;
- /* should adjust chance by morale etc */
- if (probability(chance)) {
- if (retreat_unit(other)) {
- notify(as, "%s runs away!", unit_handle(as, other));
- notify(os, "%s runs away!", unit_handle(os, other));
- hit = 0;
- }
- }
- }
- if (other == omain) {
- ohit = hit;
- } else if (other == amain) {
- ahit = hit;
- } else {
- if (hit >= other->hp) {
- occkills[o]++;
- kill_unit(other, COMBAT);
- for_all_unit_types(i) occkills[i] += occdeath[i];
- } else if (hit > 0) {
- occhits[o]++;
- other->hp -= hit;
- }
- if (utypes[a].selfdestruct) {
- kill_unit(atker, COMBAT);
- }
- }
- return (alive(other) && hit > 0);
- }
-
- /* Hits on the main units have to be done later, so that mutual */
- /* destruction is possible. This function also does all the notifying. */
- /* (Only the main units of a hex rate messages, occupants' fates are */
- /* summarized briefly.) */
-
- reckon_damage()
- {
- int o = omain->type, a = amain->type, i;
- Side *as = amain->side, *os = omain->side;
-
- strcpy(aabuf, unit_handle(as, amain));
- strcpy(aobuf, unit_handle(as, omain));
- strcpy(oabuf, unit_handle(os, amain));
- strcpy(oobuf, unit_handle(os, omain));
-
- if (ahit > 0) draw_blast(amain, omain->side, ahit);
- if (ohit > 0) draw_blast(omain, amain->side, ohit);
- if (ohit >= omain->hp) {
- notify(as, "%s %s %s!", aabuf, utypes[o].destroymsg, aobuf);
- notify(os, "%s %s %s!", oabuf, utypes[o].destroymsg, oobuf);
- kill_unit(omain, COMBAT);
- for_all_unit_types(i) occkills[i] += occdeath[i];
- } else if (ohit > 0) {
- notify(as, "%s hits %s!", aabuf, aobuf);
- notify(os, "%s hits %s!", oabuf, oobuf);
- omain->hp -= ohit;
- } else {
- /* messages about missing not too useful */
- }
- summarize_units(hitbuf, occhits);
- summarize_units(killbuf, occkills);
- if (strlen(hitbuf) > 0) {
- if (strlen(killbuf) > 0) {
- notify(as, " (Also hit%s, killed%s)", hitbuf, killbuf);
- notify(os, " (%s hurt, %s killed)", hitbuf, killbuf);
- } else {
- notify(as, " (Also hit%s)", hitbuf);
- notify(os, " (%s hurt)", hitbuf);
- }
- } else {
- if (strlen(killbuf) > 0) {
- notify(as, " (Also killed%s)", killbuf);
- notify(os, " (%s killed)", killbuf);
- }
- }
- if (ahit >= amain->hp) {
- notify(as, "%s %s %s!", aobuf, utypes[a].destroymsg, aabuf);
- notify(os, "%s %s %s!", oobuf, utypes[a].destroymsg, oabuf);
- kill_unit(amain, COMBAT);
- } else if (ahit > 0) {
- notify(as, "%s hits %s!", aobuf, aabuf);
- notify(os, "%s hits %s!", oobuf, oabuf);
- amain->hp -= ahit;
- } else {
- /* messages about missing not too useful */
- }
- if (utypes[a].selfdestruct) kill_unit(amain, COMBAT);
- if (utypes[o].selfdestruct) kill_unit(omain, COMBAT);
- }
-
- /* Handle capture possibility and repulse/slaughter. */
-
- /* The chance to capture an enemy is modified by several factors. */
- /* Neutrals have a different chance to be captured, and presence of */
- /* occupants should also has an effect. Can't capture anything that is */
- /* on a kind of terrain that the capturer can't go on, unless victim has */
- /* "bridge effect". */
-
- attempt_to_capture_unit(atker, other)
- Unit *atker, *other;
- {
- int a = atker->type, o = other->type, chance;
- int ox = other->x, oy = other->y;
- Unit *occ;
- Side *as = atker->side, *os = other->side;
-
- if (alive(atker) && alive(other) && could_capture(a, o)) {
- if (impassable(atker, ox, oy) && !utypes[o].bridge[a]) return;
- chance = utypes[a].capture[o];
- if (neutral(other)) chance -= period.neutrality;
- if (utypes[a].maxquality > 0) {
- chance += ((chance * atker->quality * utypes[a].skillf) /
- utypes[a].maxquality) / 100;
- }
- for_all_occupants(other, occ) {
- chance -= (chance * utypes[occ->type].protect[o]) / 100;
- }
- if (probability(chance)) {
- capture_unit(atker, other);
- if (global.setproduct && utypes[o].maker) {
- request_new_product(other);
- other->movesleft = 1;
- move_1(other->side, other);
- }
- } else if (atker->transport != NULL &&
- (impassable(atker, ox, oy) ||
- impassable(atker, atker->x, atker->y))) {
- notify(as, "Resistance... %s was slaughtered!",
- unit_handle(as, atker));
- notify(os, "Resistance... %s was slaughtered!",
- unit_handle(os, atker));
- kill_unit(atker, COMBAT);
- } else {
- strcpy(aabuf, unit_handle(as, atker));
- notify(as, "%s repulses %s!", unit_handle(as, other), aabuf);
- strcpy(oabuf, unit_handle(os, atker));
- notify(os, "%s repulses %s!", unit_handle(os, other), oabuf);
- }
- atker->movesleft -= utypes[a].hittime;
- }
- }
-
- /* There are many consequences of a unit being captured. */
- /* If the capturer is needed as a garrison, unload any occupants first. */
- /* (what if erstwhile occupants can't stay there?) */
-
- capture_unit(unit, pris)
- Unit *unit, *pris;
- {
- int u = unit->type, u2, px = pris->x, py = pris->y, i;
- int occs[MAXUTYPES], gains[MAXUTYPES], kills[MAXUTYPES];
- Unit *occ;
- Side *ps = pris->side, *us = unit->side;
-
- for_all_unit_types(u2) occs[u2] = gains[u2] = kills[u2] = 0;
- notify(us, "You captured %s!", unit_handle(us, pris));
- notify(ps, "%s has been captured by the %s!",
- unit_handle(ps, pris), plural_form(us->name));
- if (global.setproduct) {
- set_product(pris, NOTHING);
- pris->schedule = 0;
- }
- for_all_occupants(pris, occ) {
- occs[occ->type]++;
- if (utypes[occ->type].changeside || could_capture(u, occ->type)) {
- gains[occ->type]++;
- /* side changing happens when whole unit changes */
- } else {
- kills[occ->type]++;
- kill_unit(occ, COMBAT);
- for_all_unit_types(i) kills[i] += occdeath[i];
- }
- }
- summarize_units(hitbuf, gains);
- summarize_units(killbuf, kills);
- summarize_units(spbuf, occs);
- if (strlen(hitbuf) > 0) {
- if (strlen(killbuf) > 0) {
- notify(us, " (Also captured%s, killed%s)", hitbuf, killbuf);
- } else {
- notify(us, " (Also captured%s)", hitbuf);
- }
- } else if (strlen(killbuf) > 0) {
- notify(us, " (Also killed%s)", killbuf);
- }
- if (strlen(spbuf) > 0) {
- notify(ps, " (Lost%s)", spbuf);
- }
- /* The sad event itself */
- unit_changes_side(pris, us, CAPTURE, PRISONER);
- /* Guard the prisoner */
- if (utypes[u].guard[pris->type] > 0) {
- for_all_occupants(unit, occ) {
- if (can_carry(pris, occ)) {
- leave_hex(occ);
- occupy_hex(occ, px, py);
- }
- }
- /* This may kill some of the guard's occupants unnecessarily, but */
- /* I can't think of a better solution */
- kill_unit(unit, GARRISON);
- } else if (can_carry(pris, unit)) {
- leave_hex(unit);
- occupy_hex(unit, px, py);
- } else {
- /* Capturer doesn't guard or enter, so nothing to do */
- }
- if (see_exact(ps, px, py))
- draw_hex(ps, px, py, TRUE);
- all_see_hex(px, py);
- }
-
- /* Nearly-raw combat statistics are hard to interpret, but they provide */
- /* a useful check against subjective evaluation of performance. */
-
- print_combat_results(fp, side)
- FILE *fp;
- Side *side;
- {
- int a, d;
-
- fprintf(fp,
- "Unit Performance (successes and attacks against enemy by type\n");
- fprintf(fp, " ");
- for_all_unit_types(d) {
- fprintf(fp, " %c ", utypes[d].uchar);
- }
- fprintf(fp, "\n");
- for_all_unit_types(a) {
- fprintf(fp, " %c ", utypes[a].uchar);
- for_all_unit_types(d) {
- if (side->atkstats[a][d] > 0) {
- fprintf(fp, " %3.1f ",
- ((float) side->hitstats[a][d]) /
- side->atkstats[a][d]);
- } else {
- fprintf(fp, " ");
- }
- }
- fprintf(fp, "\n ");
- for_all_unit_types(d) {
- if (side->atkstats[a][d] > 0) {
- fprintf(fp, " %3d ", side->atkstats[a][d]);
- } else {
- fprintf(fp, " ");
- }
- }
- fprintf(fp, "\n");
- }
- fprintf(fp, "\n");
- }
-